# Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
# This source file is part of the Cangjie project, licensed under Apache-2.0
# with Runtime Library Exception.
#
# See https://cangjie-lang.cn/pages/LICENSE for license information.

from os import path
import random
from itertools import product

integer_types = ['Int8', 'Int16', 'Int32', 'Int64', 'IntNative', 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'UIntNative']
number_types = integer_types + ['Float16', 'Float32', 'Float64']
other_types = ['String', 'Rune', 'Bool', 'Unit', '(Int8, Int8)', 'Array<Int8>', 'C']

types = number_types + other_types
default_value_map = {'Bool' : 'true', 'Unit' : '()', 'Array<Int8>' : '[1]', 'Rune' : "'1'", 'Object' : 'Object()',
                     'String' : '"1"', '(Int8, Int8)' : '(1i8, 1i8)', 'C' : 'C()', 'Nothing' : 'nothing()'}

def tuple_types(t : str):
  return t[t.index('(')+1:t.index(')')].split(', ')

def default_value(ty : str) -> str:
  if ty in integer_types: return '1'
  if 'Float' in ty: return '1.0'
  if ty in classes or ty in impls: return ty+'()'
  if ty in interfaces: return ty.lower()+'()'
  if '->' in ty:
    return '{%s => (%s)}' %\
    (', '.join(['x%i : %s' % (i, l[i]) for l in [tuple_types(ty)] for i in range(len(l))]), default_value(ty[ty.index('->')+2:]))
  if '(' in ty: return '(%s)' % ', '.join([default_value(t) for t in tuple_types(ty)])
  return default_value_map[ty]

dir = path.dirname(path.realpath(__file__))
path = dir + '/test_' + path.basename(dir) + '_{}.cj'
template = '''
/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

/*
  @Assertion:   4.23(2) Note that the type of the right value must be the subtype of the type of the left value.
  @Description: %s
  @Mode: %s
  @Negative: %s
  @Structure: complex-main
  @Dependencies: aux_a02.cj
  @CompileWarning: ignore
  @Comment: Auto-generated by gen.py
*/
%s
main() {{
    %s
    return 0
}}
'''

random.seed(123)
limit = 100
classes = { 'A' : 'J', 'B' : 'AJ', 'C' : 'ABJ', 'D' : 'AJ'}
interfaces = { 'I' : '', 'J' : 'I', 'K' : 'IJ', 'L' : 'I'}
impls = { 'CI' : 'I', 'CJ' : 'IJ', 'CK' : 'IJK', 'CL' : 'IL'}

subtypes = [(t, t) for t in types] + [('Nothing', t) for t in types] + [('A', 'Object')]
non_subtypes = random.sample([(t0, t1) for t0, t1 in product(types, repeat=2) if t0 != t1], limit)

def subtype(t0, t1):
  if isinstance(t0, tuple): return all([subtype(t0[i], t1[i]) for i in range(len(t0))])
  return t0 == t1 or any(t0 in c and t1 in c[t0] for c in [classes, interfaces, impls])

subtypes += [('(A, B, C)', '(%s, %s, %s)' % (t0, t1, t2))
             for t0, t1, t2 in product(classes, repeat=3) if subtype(('A', 'B', 'C'), (t0, t1, t2))]
non_subtypes += [('(A, B, C)', '(%s, %s, %s)' % (t0, t1, t2))
             for t0, t1, t2 in product(classes, repeat=3) if not subtype(('A', 'B', 'C'), (t0, t1, t2))]
subtypes += [('(A, B) -> (A, C)', '(%s, %s) -> (%s, %s)' % (t0, t1, t2, t3))
    for t0, t1, t2, t3 in product(classes, repeat=4) if subtype((t0, t1), ('A', 'B')) and subtype(('A', 'C'), (t2, t3))]
non_subtypes += [('(A, B) -> (A, C)', '(%s, %s) -> (%s, %s)' % (t0, t1, t2, t3))
    for t0, t1, t2, t3 in product(classes, repeat=4) if not (subtype((t0, t1), ('A', 'B')) and subtype(('A', 'C'), (t2, t3)))]

def product_list(tuple_list: list):
  return [list(product(x, y)) for x, y in tuple_list]

for c in product_list([(interfaces, interfaces), (classes, classes), (impls, interfaces), (classes, interfaces)]):
    subtypes += [(t0, t1) for t0, t1 in c if subtype(t0, t1)]
    non_subtypes += [(t0, t1) for t0, t1 in c if not subtype(t0, t1)]

negative_template = template % ('Checks that a value of type {t0} cannot be assigned to a value of {t1}.', 'compileonly', 'yes', '', '''
    var rhs : {t0} = {}
    let lhs : {t1} = {}
    lhs = rhs''')
positive_test = template.format() % ('''Checks that a value of type T1 can be assigned to a value of T2 for various T1 and T2,
                where T1 is a subtype of T2.''', 'run', 'no', '''
func assign<T1, T2>(x : T1, y : T2) where T1 <: T2 {
    var v = y
    v = x
}
''', '\n    '.join(['assign<%s, %s>(%s, %s)' % (t0, t1, default_value(t0), default_value(t1)) for t0, t1 in subtypes]))
counter = 1

def write_counted(contents : str):
  global counter
  with open(path.format(str(counter).zfill(3)), 'w') as file:
    file.write(contents)
    counter += 1

write_counted(positive_test)
for test in [negative_template.format(default_value(t0), default_value(t1), t0=t0, t1=t1) for t0, t1 in non_subtypes]:
  write_counted(test)
